//Project name: personal-diary-bm
//Author: Brunner Márton (YV2X56)

//Ebben a fájlban a naplóbejegyzés osztályok és az azokkal kapcsolatos metódusok lesznek implementálva
//Ehhez először bevezetjük a saját dátumosztályunkat, majd a bejegyzés ősosztályt végül az egyes speciális alosztályokat.

#ifndef BMDIARY_HPP
#define BMDIARY_HPP

#include <string>
#include <iostream>
#include "menu.hpp"

//Dátum-osztály megvalósítása
struct Date{
  private:
    static const int min_year = 1900; //A megadható legkisebb évszám
    static const int max_year = 2200; //A megadható legnagyobb évszám
    int year, month, day;
    //A dátumról eldönti, hogy a nap legális napja-e az adott hónapnak
    bool isLegalDayOfMonth();
  public:
    //Getter metódusok az adatok elérésére, ilyen módon a tárolt nap nem módosítható, csak lekérdezhető
    inline int getDay() const{return day;}
    inline int getMonth() const{return month;}
    inline int getYear() const{return year;}
    //Az adatok megadásával való létrehozást ellátó konstruktor
    inline Date(const int &init_year,const int &init_month, const int &init_day) : year(init_year), month(init_month), day(init_day){};
    //Copy-konstruktor (bár valószínüleg a default copy-konstruktor is helyesen elvégezné ezt)
    inline Date(const Date &d) : year(d.year), month(d.month), day(d.day){};
    //Paramáter nélküli kontruktor, mivel ilyet is meg kell később hívni
    inline Date(){};
    //A megadott stream-ről beolvasó metódus
    friend std::istream& operator>>(std::istream& s, Date& d);
    //A megadott stream-re kiírja a tárolt dátumot. (Nem emel sort a kiíratás végén!)
    inline friend std::ostream& operator<<(std::ostream& s, const Date& d){
        s << d.year << ".";
        if(d.month<10) s << "0";
        s << d.month << ".";
        if(d.day<10) s << 0;
        s << d.day << ".";
        return s;
    }
};

//Bejegyzés-õsosztály megvalósítása
//Ilyen osztályt nem fogunk példányosítani, csak a közös ős miatt kell, ilyen típusú pointer elemekben fogjuk tárolni az alosztály példányait
class Entry{
  protected:
    //A név és a dátum mező a közös a három bejegyzéstípusban
    std::string name;
    Date date;
  public:
    //Getter metódusok az adatok elérésére (módosítás nem megengedett)
    inline Date getDate() const{return date;}
    inline std::string getName() const{return name;}
    //Az adatok megadásával való konstruáló metódus
    inline Entry(const std::string &init_name, const Date &init_date) : name(init_name), date(init_date){};
    //Üres paraméterlistájú konstruktor
    inline Entry(){};
    //A típusra vonatkozó számmal visszatérő metódus (mivel mindig valamelyik alosztály példányát fogjuk tárolni
    //  ilyen típusú pointerben ezért, mindig az aktuális dinamikus típus alapján hívódik meg.)
    virtual inline int EntryType(){return 0;};
};

//A rövid bejegyzés implementálása
//Az ősosztály mezőin túl egy szöveg résszel is rendelkezik. Típusazonosítója: 1
class ShortEntry : public Entry{
  private:
    std::string text;
  public:
    //Szöveg getter
    inline std::string getText() const{return text;}
    //Megadott alapértékekkel való konstruktor
    inline ShortEntry(const std::string &init_name, const Date &init_date, const std::string &init_text) : Entry(init_name, init_date), text(init_text){}
    //A felhasználótól adatokat bekérő konstruktor
    ShortEntry();
    //Típust azonosító metódus (1-gyel tér vissza)
    inline int EntryType(){return 1;}
};

//A hosszú szöveges üzenet implementálása
//Az ősosztály mezőin felül rövid leírással és hosszú szöveggel is rendelkezik. Típusazonosítója: 2
class LongEntry : public Entry{
  private:
    std::string text, brief_text;
    //A többsoros hosszú szöveg beolvasásakor, ezzel a karaktersorozattal lehet befejezni a beírást (lejjebb van az alapértelmezett beállítva)
    static const std::string exit_sequence;
  public:
    //Getter metódusok (módosítás nem megengedett)
    inline std::string getBrief() const{return brief_text;}
    inline std::string getText() const{return text;}
    //A szokásos adatokkal alapértékekre beállító konstruktor
    inline LongEntry(const std::string &init_name, const Date &init_date, const std::string &init_text, const std::string &init_brief) : Entry(init_name, init_date), text(init_text), brief_text(init_brief){};
    //A felhasználótól adatokat bekérő konstruktor
    LongEntry();
    //Típust azonosító metódus (2-vel tér vissza)
    inline int EntryType(){return 2;}
};

const std::string LongEntry::exit_sequence = "exit";

//A webcím bejegyzés implementálása
//Az ősosztály mezőin felül rövid leírással és webcímmel rendelkezik.
class WebAddress : public Entry{
  private:
    std::string weblink, brief_text;
  public:
    //Getter metódusok
    inline std::string getBrief() const{return brief_text;}
    inline std::string getWeblink() const{return weblink;}
    //Alapértékes konstruktor
    inline WebAddress(const std::string &init_name, const Date &init_date, const std::string &init_web, const std::string &init_brief) : Entry(init_name, init_date), weblink(init_web), brief_text(init_brief){};
    //Adatokat bekérő konstruktor
    WebAddress();
    //Típust azonosító metódus (3-al tér vissza)
    inline int EntryType(){return 3;}
};

//Ez a dátum osztály beolvasó metódusa, amely ellenőrzötten olvassa be az adatokat
std::istream& operator>>(std::istream& s, Date& d){
    do{
        d.year=userInput<int>("Kerem adja meg az evet:");
        if(d.year<d.min_year || d.year>d.max_year) std::cout << "Csak " << d.min_year << " es " << d.max_year << " kozotti evet adhat meg!\n";
    }while(d.year<d.min_year || d.year>d.max_year);
    do{
        d.month=userInput<int>("Kerem adja meg a honapot:");
        if(d.month<1 || d.month>12) std::cout << "Csak valos honapot adhat meg!\n";
    }while(d.month<1 || d.month>12);
    do{
        d.day=userInput<int>("Kerem adja meg a napot:");
        if(!d.isLegalDayOfMonth()) std::cout << "Csak ervenyes napot adhat meg!\n";
    }while(!d.isLegalDayOfMonth());
    return s;
}

//Az adott dátumról eldönti, hogy a nap lehet-e helyes
bool Date::isLegalDayOfMonth(){
    if(day>31 || day<1) return false;
    if((month==4 || month==6 || month==9 || month==11) && day>30) return false;
    if(month==2){
        if(day>29) return false;
        if(((year%400!=0 && year%100==0) || year%4!=0) && day>28) return false; //Ha nem szökőév
    }
    return true;
}

//Rövid bejegyzést felhasználótól bekért adatokkal létrehozó konstruktor
ShortEntry::ShortEntry(){
    name=userInput("Kerem adja meg a bejegyzes nevet:", 50);
    date=userInput<Date>("");
    text=userInput("Kerem adja meg a bejegyzes szoveget:", 500);
}

//Hosszú szöveges bejegyzést felhasználótól bekért adatokkal létrehozó konstruktor
LongEntry::LongEntry(){
    name=userInput("Kerem adja meg a bejegyzes nevet:", 50);
    date=userInput<Date>("");
    brief_text=userInput("Kerem adja meg a rovid leirast:", 100);
    text=userInput(("Kerem adja meg a bejegyzes szoveget: (az elso sor utan az '"+exit_sequence+"' parancsal tudja befejezni a beirast)"), 500);
    unsigned int max_length = 500-text.length();
    std::string input;
    do{
        input=userInput("", max_length);
        if(input!=exit_sequence){
            text+=('\n'+input);
            max_length-=(input.length()+1);
        }
    }while(input!=exit_sequence);
}

//Webcímet tartalmazó bejegyzést felhasználótól bekért adatokkal létrehozó konstruktor
WebAddress::WebAddress(){
    name=userInput("Kerem adja meg a bejegyzes nevet:", 50);
    date=userInput<Date>("");
    brief_text=userInput("Kerem adja meg a rovid leirast:", 100);
    do{
        weblink=userInput("Kerem adja meg a weblinket:", 500);
        if(weblink.find(" ")!=std::string::npos) std::cout << "A webcimben nem lehet szokoz! ";
    }while(weblink.find(" ")!=std::string::npos);
}

#endif // BMDIARY_HPP
